#pragma once

#include <cstdint>
#include <ctime>
#include <set>
#include <map>
#include <iostream>
#include <functional>


template<typename T, int default_expire = 5 * 60>
struct ExpireData
{
	std::int64_t timestamp = 0;

	inline std::int64_t ExpireTime(std::int64_t expire = default_expire)
	{
		return timestamp + expire;
	}
	inline bool Expire(std::int64_t now, std::int64_t expire = default_expire)
	{
		return now >= ExpireTime(expire);
	}
	inline void Active(std::int64_t now)
	{
		timestamp = now;
	}
	
	T data;
	inline const T& Get() const { return data; }
	inline T& Get() { return data; }

	inline void Set(const T& t) { data = t; }
	inline void Set(T&& t) { data = std::move(t); }
};


template <typename Key, typename Val, int default_expire = 5 * 60>
class ExpireMgr
{
private:
	using Data = ExpireData<Val, default_expire>;

public:
	inline int Expire() const 
	{
		return default_expire;
	}

public:
	inline bool Exist(const Key& key)
	{
		return m_datas.end() != m_datas.find(key);
	}

	inline Val* Get(const Key& key, std::int64_t now = time(NULL))
	{
		auto it = m_datas.find(key);
		if (it == m_datas.end())
		{
			return nullptr;
		}
		if (now && it->second.Expire(now))
		{
			return nullptr;
		}
		return &(it->second.Get());
	}

	inline Val* ActiveGet(const Key& key, std::int64_t now = time(NULL))
	{
		auto it = m_datas.find(key);
		if (it == m_datas.end())
		{
			return nullptr;
		}
		it->second.Active(now);
		return &it->second.Get();
	}

	inline Val* ForceGet(const Key& key, bool active_get = false)
	{
		auto it = m_datas.find(key);
		if (it == m_datas.end())
		{
			auto& item = m_datas[key];
			item.Active(time(NULL));
			return &item.Get();
		}
		else if (active_get)
		{
			it->second.Active(time(NULL));
		}
		return &it->second.Get();
	}

	inline void Set(const Key& key, Val&& val, std::int64_t now = time(NULL))
	{
		auto& item = m_datas[key];
		item.Set(std::move(val));
		if (now) item.Active(now);
	}
	inline void Set(const Key& key, const Val& val, std::int64_t now = time(NULL))
	{
		auto& item = m_datas[key];
		item.Set(val);
		if (now) item.Active(now);
	}

	inline Val* SetAndGet(const Key& key, Val&& val, std::int64_t now = time(NULL))
	{
		auto& item = m_datas[key];
		item.Set(std::move(val));
		if (now) item.Active(now);
		return &item.Get();
	}
	inline Val* SetAndGet(const Key& key, const Val& val, std::int64_t now = time(NULL))
	{
		auto& item = m_datas[key];
		item.Set(val);
		if (now) item.Active(now);
		return &item.Get();
	}
	inline void Remove(const Key& key)
	{
		m_datas.erase(key);
	}

	inline size_t Size() const 
	{
		return m_datas.size();
	}

public:
	inline void OnTick(std::int64_t now = time(NULL))
	{
		for (auto it = m_datas.begin(); it != m_datas.end();)
		{
			if (it->second.Expire(now))
			{
				it = m_datas.erase(it);
			}
			else 
			{
				it++;
			}
		}
	}
private:
	std::map<Key, Data> m_datas;
};



